﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows.Forms;
using Ionic.Zip;

namespace ChromiumUpdater
{
    public class Chrome
    {
        private const string LatestUrl = "http://build.chromium.org/buildbot/snapshots/chromium-rel-xp";
        private const string LogFile = "checklog.txt";
        private const string ZipName = "chrome-win32.zip";
        private const string ChromeWin32Dir = "chrome-win32";
        private const string ChromeExe = "chrome.exe";

        private string _workingFolder;

        public string WorkingFolder
        {
            get { return _workingFolder; }
            set { _workingFolder = value; }
        }
        private readonly string _userData;
        private readonly string _startTarget;

        private string _tempFile;

        // lastest build avaliable for downloading

        public int LatestBuild { get; set; }

        // build get by last check

        public int LastCheckBuild { get; set; }

        // build currently using
        int _currentBuild;

        public int CurrentBuild
        {
            get { return _currentBuild; }
            set { _currentBuild = value; }
        }

        public RunWorkerCompletedEventHandler CheckBuildCompleted;
        public RunWorkerCompletedEventHandler DownloadCompleted;
        public ProgressChangedEventHandler DownloadProgressChanged;

        readonly BackgroundWorker _bgDownloader = new BackgroundWorker();
        readonly MySettings _my = new MySettings();

        public Chrome()
        {
            _my = MySettings.Load();
            CurrentBuild = _my.CurrentBuild;

            _workingFolder = "App";
            _userData = "Data";
            _startTarget = Path.Combine(_workingFolder, ChromeExe);
            if (!Directory.Exists(_workingFolder))
            {
                Directory.CreateDirectory(_workingFolder);
            }
            //var tempDir = Path.GetTempPath();
            _tempFile = ZipName;

            // clean up older files
            //var filesShouldDelete = Directory.GetFiles(tempDir, "chrome-win32.zip*", SearchOption.TopDirectoryOnly);
            //foreach (var f in filesShouldDelete)
            //{
            //    File.Delete(f);
            //}
        }

        public int CheckNewBuild()
        {
            var bgCheckNewBuild = new BackgroundWorker();
            DoWorkEventHandler checking = (sender, e) =>
            {
                e.Result = GetLatestBuild();
            };

            bgCheckNewBuild.DoWork += checking;
            bgCheckNewBuild.RunWorkerCompleted += CheckBuildCompleted;
            LastCheckBuild = LatestBuild;
            bgCheckNewBuild.RunWorkerAsync();
            return 1;
        }

        public string DownloadBuild()
        {
            _bgDownloader.WorkerReportsProgress = true;
            _bgDownloader.WorkerSupportsCancellation = true;

            DirCleanUp();

            DoWorkEventHandler downloading = (sender, e) =>
            {
                e.Result = Download(_bgDownloader, e);
            };
            _bgDownloader.DoWork += downloading;
            _bgDownloader.ProgressChanged += DownloadProgressChanged;
            _bgDownloader.RunWorkerCompleted += DownloadCompleted;
            _bgDownloader.RunWorkerAsync();
            return string.Empty;
        }

        public string Unzipp()
        {
            using (var zip = ZipFile.Read(_tempFile))
            {
                // some dir clean up befrore extract and move
                DirCleanUp();
                foreach (var f in zip)
                {
                    f.Extract(".");
                }
                Directory.Move(ChromeWin32Dir, LatestBuild.ToString());
            }

            return string.Empty;
        }

        private void DirCleanUp()
        {
            if (Directory.Exists(ChromeWin32Dir))
            {
                Directory.Delete(ChromeWin32Dir, true);
            }

            if (Directory.Exists(LatestBuild.ToString()))
            {
                Directory.Delete(LatestBuild.ToString(), true);
            }
        }

        public bool Copy()
        {
            //return DoCopy(LatestBuild.ToString());
            try
            {
                var destFile = "App";
                if (Directory.Exists(destFile))
                    Directory.Delete(destFile);
                Directory.Move(LatestBuild.ToString(), "App");
                return true;

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }


        public bool Revert(string selectedBuild)
        {
            return DoCopy(selectedBuild);
        }

        /// <summary>
        /// Copy from a specific foldr
        /// </summary>
        /// <param name="sourceFolder">source folder</param>
        /// <returns></returns>
        private bool DoCopy(string sourceFolder)
        {
            var source = new DirectoryInfo(sourceFolder);
            var target = new DirectoryInfo(WorkingFolder);
            try
            {
                Directory.Delete(WorkingFolder, true);
                Directory.CreateDirectory(WorkingFolder);
                CopyAll(source, target);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return true;
        }

        private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
        {
            try
            {
                // Check if the target directory exists, if not, create it.
                if (Directory.Exists(target.FullName) == false)
                {
                    Directory.CreateDirectory(target.FullName);
                }

                // Copy each file into it's new directory.
                foreach (var fi in source.GetFiles())
                {
                    fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
                }

                // Copy each subdirectory using recursion.
                foreach (var diSourceSubDir in source.GetDirectories())
                {
                    var nextTargetSubDir =
                        target.CreateSubdirectory(diSourceSubDir.Name);
                    CopyAll(diSourceSubDir, nextTargetSubDir);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        public bool Start()
        {
            if (CheckChromeRunning() == false)
            {
                var pi = new ProcessStartInfo
                                          {
                                              UseShellExecute = false,
                                              FileName = _startTarget,
                                              WorkingDirectory = _workingFolder,
                                              Arguments = " --user-data-dir=\"../" + _userData + "\""
                                          };

                pi.Arguments += _my.Switches;

                if (!Directory.Exists(_userData))
                {
                    Directory.CreateDirectory(_userData);
                    pi.Arguments += " --first-run";
                }
                _currentBuild = LatestBuild;
                Process.Start(pi);
            }
            return true;
        }

        private static CheckResult GetLatestBuild()
        {
            try
            {
                var latest = GetSource(LatestUrl + "/LATEST");
                var latestRev = Convert.ToInt32(latest);
                var cr = new CheckResult { Rev = latestRev, Log = "" };

                return cr;
            }
            catch (Exception)
            {
                // just ignore exception
                return new CheckResult { Log = string.Empty, Rev = -1 };
            }
        }

        private static string GetSource(string url)
        {
            using (var webClient = new WebClient())
            {
                var source = webClient.DownloadString(url);
                return source;
            }
        }

        private bool Download(BackgroundWorker worker, DoWorkEventArgs e)
        {
            // The stream of data retrieved from the web server
            Stream strResponse;

            // The stream of data that we write to the harddrive
            Stream strLocal;

            // The request to the web server for file information
            HttpWebRequest webRequest;

            // The response from the web server containing information about the file
            HttpWebResponse webResponse;

            // The progress of the download in percentage

            int percentProgress = 0;
            if (worker.CancellationPending)
            {
                // Close the web response and the streams
                e.Cancel = true;
            }
            else
            {
                if (File.Exists(_tempFile))
                {
                    _tempFile = _tempFile + "1";
                }
                using (strLocal = new FileStream(_tempFile, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // Create a request to the file we are downloading
                    webRequest = (HttpWebRequest)WebRequest.Create(LatestUrl + "/" + LatestBuild + "/" + ZipName);

                    // Set default authentication for retrieving the file
                    webRequest.Credentials = CredentialCache.DefaultCredentials;

                    // Retrieve the response from the server
                    webResponse = (HttpWebResponse)webRequest.GetResponse();

                    // Ask the server for the file size and store it
                    var fileSize = webResponse.ContentLength;

                    // Open the URL for download 
                    using (strResponse = webResponse.GetResponseStream())
                    {
                        // It will store the current number of bytes we retrieved from the server
                        int bytesSize;

                        // A buffer for storing and writing the data retrieved from the server
                        var downBuffer = new byte[1024];

                        // Loop through the buffer until the buffer is empty
                        while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
                        {
                            // Write the data from the buffer to the local hard drive
                            strLocal.Write(downBuffer, 0, bytesSize);
                            var bytesRead = strLocal.Length;
                            var totalBytes = fileSize;
                            percentProgress = Convert.ToInt32((bytesRead * 100) / totalBytes);
                            worker.ReportProgress(percentProgress);
                        }
                    }
                }
            }
            return percentProgress == 100;
        }

        public void CancelDownload()
        {
            _bgDownloader.CancelAsync();
            var fi = new FileInfo(_tempFile);
            if (fi.Exists)
            {
                fi.Delete();
            }
        }

        public bool CheckChromeRunning()
        {
            var a = from p in Process.GetProcesses()
                    where p.ProcessName == "chrome"
                    select p;
            return a.Count() > 0 ? true : false;
        }
    }
}
